﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Validity.Rules;

namespace Validity.Test.Rules
{
  [TestClass]
  public class ComparisonRuleTest
  {
    [TestMethod]
    public void TestIsViolatedByArgSameAsArgProvided()
    {
      ObjectStub expectedObj = new ObjectStub();
      IComparable expectedVal = 100;

      var rule = new ComparisonRule<ObjectStub, IComparable>(
        obj =>
        {
          Assert.AreSame(expectedObj, obj);
          return expectedVal;
        },
        result => result == 0,
        (obj, val) => "Message");

      Assert.IsFalse(rule.IsViolatedBy(expectedObj, expectedVal));
    }

    [TestMethod]
    public void TestIsViolatedByReturnsFalseWhenConstraintEvaluatesTrue()
    {
      IComparable lhs = 1;
      IComparable rhs = 1;

      var rule = new ComparisonRule<ObjectStub, IComparable>(value => lhs, result => result == 0, (obj, val) => "My Customized Message");

      Assert.IsFalse(rule.IsViolatedBy(new ObjectStub(), rhs));
    }

    [TestMethod]
    public void TestIsViolatedByReturnsTrueWhenConstraintEvaluatesFalse()
    {
      IComparable lhs = 1;
      IComparable rhs = 1;

      var rule = new ComparisonRule<ObjectStub, IComparable>(value => lhs, result => result != 0, (obj, val) => "My Customized Message");

      Assert.IsTrue(rule.IsViolatedBy(new ObjectStub(), rhs));
    }

    [TestMethod]
    public void TestIsViolatedByEvaluatesNullLhsAndRhsAsEqual()
    {
      var rule = new ComparisonRule<ObjectStub, IComparable>(value => null, result => result == 0, (obj, val) => "My Customized Message");

      Assert.IsFalse(rule.IsViolatedBy(new ObjectStub(), null));
    }

    [TestMethod]
    public void TestIsViolatedByEvaluatesNullLhsAsLessThanNonNullRhs()
    {
      var rule = new ComparisonRule<ObjectStub, IComparable>(value => "My String", result => result < 0, (obj, val) => "My Customized Message");

      Assert.IsFalse(rule.IsViolatedBy(new ObjectStub(), null));
    }

    [TestMethod]
    public void TestIsViolatedByUsingCustomComparer()
    {
      String lhs = "MyString";
      String rhs = "myString";

      var rule = new ComparisonRule<ObjectStub, String>(value => lhs, result => result == 0, (obj, val) => "My Customized Message");

      Assert.IsTrue(rule.IsViolatedBy(new ObjectStub(), rhs));
      Assert.IsFalse(rule.IsViolatedBy(new ObjectStub(), rhs, (l, r) => String.Compare(l, r, StringComparison.OrdinalIgnoreCase)));
    }

    [TestMethod]
    public void TestGetRuleViolationArgsSameAsArgsProvided()
    {
      ObjectStub expectedObj = new ObjectStub();
      IComparable expectedVal = "";

      var rule = new ComparisonRule<ObjectStub, IComparable>(
        value => expectedVal,
        result => result == 0,
        (obj, val) =>
        {
          Assert.AreSame(expectedObj, obj);
          Assert.AreSame(expectedVal, val);

          return "Message";
        }
      );

      rule.GetRuleViolation(expectedObj, expectedVal);
    }

    [TestMethod]
    public void TestGetRuleViolationReturnsDefaultOnNullMessageFactoryResult()
    {
      var rule = new ComparisonRule<ObjectStub, IComparable>(value => 0, result => result == 0, (obj, val) => null);

      Assert.AreEqual("Value is invalid", rule.GetRuleViolation(new ObjectStub(), 0));
    }

    [TestMethod]
    public void TestGetRuleViolationReturnsNonNullMessageFactoryResult()
    {
      var rule = new ComparisonRule<ObjectStub, IComparable>(value => 0, result => result == 0, (obj, val) => "My Customized Message");

      Assert.AreEqual("My Customized Message", rule.GetRuleViolation(new ObjectStub(), 0));
    }
  }
}
